package cn.qinyun.cloud.core.auth.aspect;

import cn.qinyun.cloud.common.constant.Oauth2Constant;
import cn.qinyun.cloud.common.entity.LoginUser;
import cn.qinyun.cloud.common.exception.TokenException;
import cn.qinyun.cloud.common.util.SecurityUtil;
import cn.qinyun.cloud.common.util.StringPool;
import cn.qinyun.cloud.core.auth.annotation.PreAuth;
import cn.qinyun.cloud.core.redis.core.RedisService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 类名称：PreAuthAspect <br>
 * 类描述：TODO <br>
 *
 * @author 张兆发
 * @version 1.0.0
 * @date 创建时间：2021/7/16  2:13 下午 <br>
 */
@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class PreAuthAspect {

    /**
     * 所有权限标识
     */
    private static final String ALL_PERMISSION = "*:*:*";

    private final HttpServletRequest request;

    private final RedisService redisService;

    @Around("@annotation(cn.qinyun.cloud.core.auth.annotation.PreAuth)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        PreAuth preAuth = method.getAnnotation(PreAuth.class);
        if (ObjectUtils.isEmpty(preAuth)) {
            return point.proceed();
        }

        if (hasPerm(preAuth.hasPerm())) {
            return point.proceed();
        } else {
            throw new TokenException("权限验证不通过");
        }
    }


    /**
     * 验证用户是否具备某权限
     *
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPerm(String permission) {
        LoginUser userInfo = SecurityUtil.getUsername(request);

        if (StringUtils.isEmpty(userInfo)) {
            return false;
        }

        if (!StringUtils.isEmpty(userInfo) && StringUtils.isEmpty(permission)) {
            return true;
        }

        // 如果用户是超级管理员，则直接跳过权限验证
        if (userInfo.getAccount().equalsIgnoreCase(Oauth2Constant.SUPER_ADMIN)) {
            return true;
        }
        Map<String, Object> data = (Map<String, Object>) redisService.get(Oauth2Constant.MATE_PERMISSION_PREFIX
                + userInfo.getAccount() + StringPool.DOT + userInfo.getRoleId());
        List<String> authorities = (List<String>) data.get("permissions");
        return hasPermissions(authorities, permission);
    }

    /**
     * 判断是否包含权限
     *
     * @param authorities 权限列表
     * @param permission  权限字符串
     * @return 用户是否具备某权限
     */
    private boolean hasPermissions(Collection<String> authorities, String permission) {
        return authorities.stream().filter(StringUtils::hasText)
                .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x));
    }

}
